package com.ikingtech.platform.service.system.user.service.repository;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.enums.system.user.UserLockTypeEnum;
import com.ikingtech.framework.sdk.user.api.UserDeptApi;
import com.ikingtech.framework.sdk.user.api.UserPostApi;
import com.ikingtech.framework.sdk.user.api.UserRoleApi;
import com.ikingtech.framework.sdk.user.model.*;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.platform.service.system.user.entity.*;
import lombok.RequiredArgsConstructor;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author tie yan
 */
@RequiredArgsConstructor
public class ModelConverter {

    private final UserSocialRepository userSocialRepo;

    private final UserCategoryRepository userCategoryRepo;

    private final UserDeptRepository userDeptRepo;

    private final UserRoleRepository userRoleRepo;

    private final UserPostRepository userPostRepo;

    private final UserDeptApi userDeptApi;

    private final UserRoleApi userRoleApi;

    private final UserPostApi userPostApi;

    public List<UserDTO> modelBatchConvert(List<UserDO> entities) {
        if (Tools.Coll.isBlank(entities)) {
            return Collections.emptyList();
        }
        List<String> userIds = Tools.Coll.convertList(entities, UserDO::getId);
        List<UserRoleDO> userRoleEntities = this.userRoleRepo.list(Wrappers.<UserRoleDO>lambdaQuery()
                .in(UserRoleDO::getUserId, userIds)
                .eq(Tools.Str.isNotBlank(Me.domainCode()), UserRoleDO::getDomainCode, Me.domainCode())
                .eq(Tools.Str.isNotBlank(Me.tenantCode()), UserRoleDO::getTenantCode, Me.tenantCode())
                .eq(Tools.Str.isNotBlank(Me.appCode()), UserRoleDO::getAppCode, Me.appCode()));
        List<UserDeptDO> userDeptEntities = this.userDeptRepo.list(Wrappers.<UserDeptDO>lambdaQuery()
                .in(UserDeptDO::getUserId, userIds)
                .eq(UserDeptDO::getTenantCode, Me.tenantCode()));
        List<UserPostDO> userPostEntities = this.userPostRepo.list(Wrappers.<UserPostDO>lambdaQuery()
                .in(UserPostDO::getUserId, userIds)
                .eq(UserPostDO::getTenantCode, Me.tenantCode()));
        List<UserSocialDO> userSocialEntities = this.userSocialRepo.list(Wrappers.<UserSocialDO>lambdaQuery().in(UserSocialDO::getUserId, userIds));
        List<UserCategoryDO> userCategoryEntities = this.userCategoryRepo.list(Wrappers.<UserCategoryDO>lambdaQuery()
                .in(UserCategoryDO::getUserId, userIds)
                .eq(Tools.Str.isNotBlank(Me.tenantCode()), UserCategoryDO::getTenantCode, Me.tenantCode())
                .and(Tools.Str.isBlank(Me.tenantCode()), wrapper -> wrapper.isNull(UserCategoryDO::getTenantCode).or().eq(UserCategoryDO::getTenantCode, Tools.Str.EMPTY)));
        return this.modelBatchConvert(entities,
                userDeptEntities,
                userRoleEntities,
                userPostEntities,
                userSocialEntities,
                userCategoryEntities);
    }

    public List<UserDTO> modelBatchConvert(List<UserDO> entities,
                                           List<UserDeptDO> userDeptEntities,
                                           List<UserRoleDO> userRoleEntities,
                                           List<UserPostDO> userPostEntities,
                                           List<UserSocialDO> userSocialEntities,
                                           List<UserCategoryDO> userCategoryEntities) {
        Map<String, List<String>> userDeptIdMap = Tools.Coll.convertGroup(userDeptEntities, UserDeptDO::getUserId, UserDeptDO::getDeptId);
        List<UserDeptDTO> departments = this.userDeptApi.loadByIds(Tools.Coll.convertList(userDeptEntities, UserDeptDO::getDeptId));
        Map<String, UserDeptDTO> deptMap = Tools.Coll.convertMap(departments, UserDeptDTO::getDeptId, dept -> dept);

        Map<String, List<String>> userRoleIdMap = Tools.Coll.convertGroup(userRoleEntities, UserRoleDO::getUserId, UserRoleDO::getRoleId);
        List<UserRoleDTO> roles = this.userRoleApi.loadByIds(Tools.Coll.convertList(userRoleEntities, UserRoleDO::getRoleId));
        Map<String, UserRoleDTO> roleMap = Tools.Coll.convertMap(roles, UserRoleDTO::getRoleId, role -> role);

        Map<String, List<String>> userPostIdMap = Tools.Coll.convertGroup(userPostEntities, UserPostDO::getUserId, UserPostDO::getPostId);
        List<UserPostDTO> posts = this.userPostApi.loadByIds(Tools.Coll.convertList(userPostEntities, UserPostDO::getPostId));
        Map<String, UserPostDTO> postMap = Tools.Coll.convertMap(posts, UserPostDTO::getPostId, post -> post);

        Map<String, List<UserSocialDO>> userSocialMap = Tools.Coll.convertGroup(userSocialEntities, UserSocialDO::getUserId);
        Map<String, List<UserCategoryDO>> userCategoryMap = Tools.Coll.convertGroup(userCategoryEntities, UserCategoryDO::getUserId);


        return Tools.Coll.convertList(entities, entity -> this.modelConvert(entity,
                Tools.Coll.convertList(userDeptIdMap.get(entity.getId()), deptMap::containsKey, deptMap::get),
                Tools.Coll.convertList(userRoleIdMap.get(entity.getId()), roleMap::containsKey, roleMap::get),
                Tools.Coll.convertList(userPostIdMap.get(entity.getId()), postMap::containsKey, postMap::get),
                userSocialMap.get(entity.getId()),
                userCategoryMap.get(entity.getId())));
    }

    public UserDTO modelConvert(UserDO entity) {
        List<String> userRoleIds = this.userRoleRepo.listObjs(Wrappers.<UserRoleDO>lambdaQuery()
                .select(UserRoleDO::getRoleId)
                .eq(UserRoleDO::getUserId, entity.getId())
                .eq(UserRoleDO::getTenantCode, Me.tenantCode()));
        List<String> userDeptIds = this.userDeptRepo.listObjs(Wrappers.<UserDeptDO>lambdaQuery()
                .select(UserDeptDO::getDeptId)
                .in(UserDeptDO::getUserId, entity.getId()));
        List<String> userPostIds = this.userPostRepo.listObjs(Wrappers.<UserPostDO>lambdaQuery()
                .select(UserPostDO::getPostId)
                .eq(UserPostDO::getUserId, entity.getId())
                .eq(UserPostDO::getTenantCode, Me.tenantCode()));
        List<UserSocialDO> userSocialEntities = this.userSocialRepo.list(Wrappers.<UserSocialDO>lambdaQuery()
                .eq(UserSocialDO::getUserId, entity.getId()));
        List<UserCategoryDO> userCategoryEntities = this.userCategoryRepo.list(Wrappers.<UserCategoryDO>lambdaQuery()
                .eq(UserCategoryDO::getUserId, entity.getId()));
        List<UserDeptDTO> departments = this.userDeptApi.loadByIds(userDeptIds);
        List<UserRoleDTO> roles = this.userRoleApi.loadByIds(userRoleIds);
        List<UserPostDTO> posts = this.userPostApi.loadByIds(userPostIds);

        return this.modelConvert(entity, departments, roles, posts, userSocialEntities, userCategoryEntities);
    }

    public UserDTO modelConvert(UserDO entity,
                                List<UserDeptDTO> departments,
                                List<UserRoleDTO> roles,
                                List<UserPostDTO> posts,
                                List<UserSocialDO> userSocialEntities,
                                List<UserCategoryDO> userCategoryEntities) {
        UserDTO user = Tools.Bean.copy(entity, UserDTO.class);
        user.setDeptIds(Tools.Coll.convertList(departments, UserDeptDTO::getDeptId));
        user.setDepartments(departments);

        user.setRoleIds(Tools.Coll.convertList(roles, UserRoleDTO::getRoleId));
        user.setRoles(roles);

        user.setPostIds(Tools.Coll.convertList(posts, UserPostDTO::getPostId));
        user.setPosts(posts);

        user.setCategories(Tools.Coll.convertList(userCategoryEntities, this::convertUserCategory));
        user.setCategoryCodes(Tools.Coll.convertList(userCategoryEntities, userCategoryEntity -> Tools.Str.isNotBlank(userCategoryEntity.getCategoryCode()), UserCategoryDO::getCategoryCode));

        user.setSocials(Tools.Coll.convertList(userSocialEntities, socialEntity -> Tools.Bean.copy(socialEntity, UserSocialDTO.class)));
        user.setLockTypeName(UserLockTypeEnum.valueOf(entity.getLockType()).description);
        return user;
    }

    public UserBasicDTO modelInfoConvert(UserDO entity) {
        return this.modelInfoConvert(entity, this.userSocialRepo.list(Wrappers.<UserSocialDO>lambdaQuery().eq(UserSocialDO::getUserId, entity.getId())), this.userCategoryRepo.list(Wrappers.<UserCategoryDO>lambdaQuery().eq(UserCategoryDO::getUserId, entity.getId())));
    }

    public List<UserBasicDTO> modelInfoConvert(List<UserDO> entities) {
        Map<String, List<UserSocialDO>> userSocialMap = new HashMap<>();
        Map<String, List<UserCategoryDO>> userCategoryMap = new HashMap<>();
        if (Tools.Coll.isNotBlank(entities)) {
            List<UserSocialDO> userSocialEntities = this.userSocialRepo.list(Wrappers.<UserSocialDO>lambdaQuery().in(UserSocialDO::getUserId, Tools.Coll.convertList(entities, UserDO::getId)));
            userSocialMap.putAll(Tools.Coll.convertGroup(userSocialEntities, UserSocialDO::getUserId));

            List<UserCategoryDO> userCategoryEntities = this.userCategoryRepo.list(Wrappers.<UserCategoryDO>lambdaQuery().in(UserCategoryDO::getUserId, Tools.Coll.convertList(entities, UserDO::getId)));
            userCategoryMap.putAll(Tools.Coll.convertGroup(userCategoryEntities, UserCategoryDO::getUserId));
        }
        return Tools.Coll.convertList(entities, entity -> this.modelInfoConvert(entity, userSocialMap.get(entity.getId()), userCategoryMap.get(entity.getId())));
    }

    public UserBasicDTO modelInfoConvert(UserDO entity, List<UserSocialDO> userSocialEntities, List<UserCategoryDO> userCategoryEntities) {
        UserBasicDTO user = Tools.Bean.copy(entity, UserBasicDTO.class);
        user.setSocials(Tools.Coll.convertList(userSocialEntities, socialEntity -> Tools.Bean.copy(socialEntity, UserSocialDTO.class)));

        user.setCategories(Tools.Coll.convertList(userCategoryEntities, this::convertUserCategory));
        user.setCategoryCodes(Tools.Coll.convertList(userCategoryEntities, userCategoryEntity -> Tools.Str.isNotBlank(userCategoryEntity.getCategoryCode()), UserCategoryDO::getCategoryCode));
        user.setLockTypeName(UserLockTypeEnum.valueOf(entity.getLockType()).description);
        return user;
    }

    public UserCategoryDTO convertUserCategory(UserCategoryDO userCategoryEntity) {
        UserCategoryDTO userCategory = Tools.Bean.copy(userCategoryEntity, UserCategoryDTO.class);
        userCategory.setCategoryName(userCategory.getCategoryCode().description);
        return userCategory;
    }
}
